/*
 * The contents of this file are subject to the Mozilla Public License
 * Version 1.1 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations
 * under the License.
 * 
 * The Original Code is iSQL-Viewer, A Mutli-Platform Database Tool.
 *
 * The Initial Developer of the Original Code is iSQL-Viewer, A Mutli-Platform Database Tool.
 * Portions created by Mark A. Kobold are Copyright (C) 2000-2007. All Rights Reserved.
 *
 * Contributor(s): 
 *  Mark A. Kobold [mkobold <at> isqlviewer <dot> com].
 *  
 * If you didn't download this code from the following link, you should check
 * if you aren't using an obsolete version: http://www.isqlviewer.com
 */
package org.isqlviewer.util;

import java.util.Date;

/**
 * General utilities class for common data validation patterns and assertions.
 * <p>
 * Some of these methods are variations on the themes in JUnit however the exceptions thrown herein are more appropriate
 * for runtime situations like {@link NullPointerException}, and {@link IllegalArgumentException}. Where as most
 * applications are not actively catching an AssertionFailedError which can fall through an Exception trap.
 * <p>
 * The NULL_XXX constants defined within this class also help facilitate the usage of Object versions of primitive
 * data-types, where normally primitives cannot be null, and typically a -1 or NaN would represent a null primitive.
 * These object instances assist with code-readability when dealing with those types of situations.
 * 
 * @author Mark A. Kobold
 * @version 1.0
 */
public final class Assertions {

    /**
     * User constant for defining an null primitive long value.
     * <p>
     * <code>(Assertions.NULL_LONG.longValue() == -1) == true</code>
     */
    public static final Long NULL_LONG = new Long(-1);

    /**
     * User constant for defining an null primitive integer value.
     * <p>
     * <code>(Assertions.NULL_INTEGER.intValue() == -1) == true</code>
     */
    public static final Integer NULL_INTEGER = new Integer(-1);

    /**
     * User constant for defining an null primitive short value.
     * <p>
     * <code>(Assertions.NULL_SHORT.shortValue() == -1) == true</code>
     */
    public static final Short NULL_SHORT = new Short((short) -1);

    /**
     * User constant for defining an null primitive byte value.
     * <p>
     * <code>(Assertions.NULL_BYTE.byteValue() == -1) == true</code>
     */
    public static final Byte NULL_BYTE = new Byte((byte) -1);

    /**
     * User constant for defining an null primitive float value.
     * <p>
     * <code>Float.isNaN(Assertions.NULL_FLOAT) == true</code>
     */
    public static final Float NULL_FLOAT = new Float(Float.NaN);

    /**
     * User constant for defining an null primitive double value.
     * <p>
     * <code>Double.isNaN(Assertions.NULL_DOUBLE) == true</code>
     */
    public static final Double NULL_DOUBLE = new Double(Double.NaN);

    /**
     * User constant for defining an null primitive character value.
     * <p>
     * <code>(Assertions.NULL_CHARACTER.charValue() == 0) == true</code>
     */
    public static final Character NULL_CHARACTER = new Character((char) 0);

    /**
     * User constant for defining an null primitive boolean value.
     * <p>
     * <code>Assertions.NULL_BOOLEAN.booleanValue() == false</code>
     */
    public static final Boolean NULL_BOOLEAN = Boolean.FALSE;

    /**
     * User constant for defining an null date.
     * <p>
     * <code>(Assertions.NULL_DATE.getTime() == 0) == true</code>
     * <p>
     * Though this isn't a primitive type, it is effectively a wrapper for a long value,plus it is common data-type.
     */
    public static final Date NULL_DATE = new Date(0);

    private static final String RESOURCE_BUNDLE = "org.isqlviewer.util.ResourceBundle";
    private static final LocalMessages messages = new LocalMessages(RESOURCE_BUNDLE);

    private Assertions() {

    }

    /**
     * Checks to ensure that the value is not less than zero.
     * <p>
     * Utility method for ensuring that the given value is positive otherwise throw an exception. This method version
     * assumes no logical identifier with the given argument.
     * 
     * @param value to check for positiveness.
     * @throws IllegalArgumentException if the given value is less than zero.
     * @throws NullPointerException if the given value is null.
     */
    public static void assertPositive(Number value) {

        assertPositive(null, value);
    }

    /**
     * Checks to ensure that the value is not less than zero.
     * <p>
     * Utility method for ensuring that the given value is positive otherwise throw an exception.
     * 
     * @param identifier a logical name that will accompany the exception if generated; can be <tt>null</tt>.
     * @param value to check for positiveness.
     * @throws IllegalArgumentException if the given value is less than zero.
     */
    public static void assertPositive(String identifier, double value) {

        if (value < 0) {
            String err = null;
            if (identifier != null) {
                err = messages.format("assertions.known_id_must_be_positive", identifier, Double.toString(value));
            } else {
                err = messages.format("assertions.unknown_id_must_be_positive", Double.toString(value));
            }
            throw new IllegalArgumentException(err);
        }
    }

    /**
     * Checks to ensure that a Number value is not less than zero.
     * <p>
     * Utility method for ensuring that the given value is positive otherwise throw an exception.
     * 
     * @param identifier a logical name that will accompany the exception if generated; can be <tt>null</tt>.
     * @param value to check for positiveness.
     * @throws IllegalArgumentException if the given value is less than zero.
     * @throws NullPointerException if the given number value is null.
     */
    public static void assertPositive(String identifier, Number value) {

        assertNotNull(identifier, value);
        assertPositive(value.doubleValue());
    }

    /**
     * Checks to ensure that the value is not less than zero.
     * <p>
     * Utility method for ensuring that the given value is positive otherwise throw an exception. This method version
     * assumes no logical identifier with the given argument.
     * 
     * @param value to check for positiveness.
     * @throws IllegalArgumentException if the given value is less than zero.
     */
    public static void assertPositive(double value) {

        assertPositive(null, value);
    }

    /**
     * Checks to ensure that the value is not less than or equal zero.
     * <p>
     * Utility method for ensuring that the given value is positive excluding zero, otherwise throw an exception.
     * 
     * @param value to check for positiveness.
     * @throws IllegalArgumentException if the given value is less than or equal zero.
     */
    public static void assertPositiveExcludeZero(double value) {

        assertPositiveExcludeZero(null, value);
    }

    /**
     * Checks to ensure that the value is not less than or equal zero.
     * <p>
     * Utility method for ensuring that the given value is positive excluding zero, otherwise throw an exception.
     * 
     * @param value to check for positiveness.
     * @throws IllegalArgumentException if the given value is less than or equal zero.
     */
    public static void assertPositiveExcludeZero(Number value) {

        assertPositiveExcludeZero(null, value);
    }

    /**
     * Checks to ensure that the value is not less than or equal zero.
     * <p>
     * Utility method for ensuring that the given value is positive excluding zero, otherwise throw an exception.
     * 
     * @param identifier a logical name that will accompany the exception if generated; can be <tt>null</tt>.
     * @param value to check for positiveness.
     * @throws IllegalArgumentException if the given value is less than or equal zero.
     */
    public static void assertPositiveExcludeZero(String identifier, double value) {

        if (value <= 0) {
            String err = null;
            if (identifier != null) {
                err = messages.format("assertions.known_id_must_be_positive", identifier, Double.toString(value));
            } else {
                err = messages.format("assertions.unknown_id_must_be_positive", Double.toString(value));
            }
            throw new IllegalArgumentException(err);
        }
    }

    /**
     * Checks to ensure that the value is not less than or equal zero.
     * <p>
     * Utility method for ensuring that the given value is positive excluding zero, otherwise throw an exception.
     * 
     * @param identifier a logical name that will accompany the exception if generated; can be <tt>null</tt>.
     * @param value to check for positiveness.
     * @throws IllegalArgumentException if the given value is less than or equal zero.
     * @throws NullPointerException if the given number value is null.
     */
    public static void assertPositiveExcludeZero(String identifier, Number value) {

        assertNotNull(identifier, value);
        assertPositiveExcludeZero(identifier, value.doubleValue());
    }

    /**
     * Check to make sure that given value by name is not null.
     * <p>
     * 
     * @param identifier a logical name that will accompany the exception if generated; can be <tt>null</tt>.
     * @param value to check for null.
     * @throws NullPointerException if the given value is null.
     */
    public static void assertNotNull(String identifier, Object value) {

        if (value == null) {
            String err = null;
            if (identifier != null) {
                err = messages.format("assertions.known_id_cannot_be_null", identifier);
            } else {
                err = messages.format("assertions.unknown_id_cannot_be_null");
            }
            throw new NullPointerException(err);
        }
    }

    /**
     * Check to make sure that given value is not null.
     * <p>
     * 
     * @param value to check for null.
     * @throws NullPointerException if the given value is null.
     */
    public static void assertNotNull(Object value) {

        assertNotNull(null, value);
    }

    /**
     * Check to make sure that given value by name is null.
     * <p>
     * 
     * @param identifier a logical name that will accompany the exception if generated; can be <tt>null</tt>.
     * @param value to check for null.
     * @throws IllegalArgumentException if the given value is not null.
     */
    public static void assertNull(String identifier, Object value) {

        if (value != null) {
            String err = null;
            if (identifier != null) {
                err = messages.format("assertions.known_id_must_be_null", identifier);
            } else {
                err = messages.format("assertions.unknown_id_must_be_null");
            }
            throw new IllegalArgumentException(err);
        }
    }

    /**
     * Check to make sure that given value is null.
     * <p>
     * 
     * @param value to check for null.
     * @throws IllegalArgumentException if the given value is not null.
     */
    public static void assertNull(Object value) {

        assertNull(null, value);
    }

    /**
     * Checks to ensure that the given value is less than or equal to given maximum value.
     * <p>
     * 
     * @param identifier a logical name that will accompany the exception if generated; can be <tt>null</tt>.
     * @param value to assert that is less or equal to the maximum value.
     * @param maximumValue that the given value is allowed to be.
     * @throws IllegalArgumentException if the value is greater than the maximum value.
     */
    public static void assertLessThanEqual(String identifier, double value, double maximumValue) {

        if (value > maximumValue) {
            String err = null;
            String maxString = Double.toString(maximumValue);
            String valueString = Double.toString(value);
            if (identifier != null) {
                err = messages.format("assertions.known_id_value_too_large", identifier, valueString, maxString);
            } else {
                err = messages.format("assertions.unknown_id_value_too_large", valueString, maxString);
            }
            throw new IllegalArgumentException(err);
        }
    }

    /**
     * Checks to ensure that the given value is less than or equal to given maximum value.
     * <p>
     * 
     * @param value to assert that is less or equal to the maximum value.
     * @param maximumValue that the given value is allowed to be.
     * @throws IllegalArgumentException if the value is greater than the maximum value.
     */
    public static void assertLessThanEqual(double value, double maximumValue) {

        assertLessThanEqual(null, value, maximumValue);
    }

    /**
     * Checks to ensure that the given value is greater than or equal to minimum required value.
     * <p>
     * 
     * @param identifier a logical name that will accompany the exception if generated; can be <tt>null</tt>.
     * @param value to assert that is less or equal to the maximum value.
     * @param minimumValue the smallest value allowed.
     * @throws IllegalArgumentException if the value is greater than the maximum value.
     */
    public static void assertGreaterThanEqual(String identifier, double value, double minimumValue) {

        if (value < minimumValue) {
            String err = null;
            String minString = Double.toString(minimumValue);
            String valueString = Double.toString(value);
            if (identifier != null) {
                err = messages.format("assertions.known_id_value_too_small", identifier, valueString, minString);
            } else {
                err = messages.format("assertions.unknown_id_value_too_small", valueString, minString);
            }
            throw new IllegalArgumentException(err);
        }
    }

    /**
     * Checks to ensure that the given value is greater than or equal to minimum required value.
     * <p>
     * 
     * @param value to assert that is less or equal to the maximum value.
     * @param minimumValue the smallest value allowed.
     * @throws IllegalArgumentException if the value is greater than the maximum value.
     */
    public static void assertGreaterThanEqual(double value, double minimumValue) {

        assertGreaterThanEqual(null, value, minimumValue);

    }

    /**
     * Asserts that the expected value is equal to the actual value.
     * <p>
     * 
     * @param string identifier for this assertion; can be null.
     * @param expected value of the actualValue variable.
     * @param actualValue to verify against the expected value.
     * @throws IllegalArgumentException if the values are not equal.
     */
    public static void assertEqual(String identifier, double expected, double actualValue) {

        if (expected != actualValue) {
            String err = null;
            String expectation = Double.toString(expected);
            String actual = Double.toString(actualValue);
            if (identifier != null) {
                err = messages.format("assertions.known_id_value_not_equal", identifier, actual, expectation);
            } else {
                err = messages.format("assertions.unknown_id_value_not_equal", actual, expectation);
            }
            throw new IllegalArgumentException(err);
        }
    }

    /**
     * Asserts that the expected value is equal to the actual value.
     * <p>
     * 
     * @param expected value of the actualValue variable.
     * @param actualValue to verify against the expected value.
     * @throws IllegalArgumentException if the values are not equal.
     */
    public static void assertEqual(double expected, double actualValue) {

        assertEqual(null, expected, actualValue);
    }
}